package cn.virens.common.util.core.uuid;


import cn.virens.common.util.exception.APIException;

public final class UuidUtil {
    private static final char[] DICT = "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray();
    private static final ThreadLocal<UuidUtil> local = new ThreadLocal<>();

    private static final long SETP7 = (long) Math.pow(DICT.length, 7);
    private static final long SETP6 = (long) Math.pow(DICT.length, 6);
    private static final long SETP5 = (long) Math.pow(DICT.length, 5);
    private static final long SETP4 = (long) Math.pow(DICT.length, 4);
    private static final long SETP3 = (long) Math.pow(DICT.length, 3);
    private static final long SETP2 = (long) Math.pow(DICT.length, 2);
    private static final long SETP1 = (long) Math.pow(DICT.length, 1);
    private static final long SETP0 = (long) Math.pow(DICT.length, 0);

    private final char prefix3; // 线程号2
    private final char prefix2; // 线程号1
    private final char prefix1; // 线程号0
    private final char prefix0; // 服务号

    private long order = 0;

    private UuidUtil(long serverId) throws APIException {
        this(threadId(), serverId);
    }

    private UuidUtil(long threadId, long serverId) throws APIException {
        this.prefix3 = getCharValue(threadId, SETP0);
        this.prefix2 = getCharValue(threadId, SETP1);
        this.prefix1 = getCharValue(threadId, SETP2);
        this.prefix0 = getCharValue(serverId, SETP0);
    }

    /**
     * 生成唯一序列号
     */
    public String generateCode() throws APIException {
        return gen(serialId(), currentId(), new char[16]);
    }

    /**
     * 根据序列号号、时间戳生成填充
     */
    private String gen(long serialId, long currentId, char[] answer) {
        answer[15] = getCharValue(serialId, SETP0); // 填充序列号0
        answer[14] = getCharValue(serialId, SETP1); // 填充序列号1
        answer[13] = getCharValue(serialId, SETP2); // 填充序列号2
        answer[12] = getCharValue(serialId, SETP3); // 填充序列号3

        answer[11] = getCharValue(currentId, SETP0); // 填充时间戳0
        answer[10] = getCharValue(currentId, SETP1); // 填充时间戳1
        answer[9] = getCharValue(currentId, SETP2); // 填充时间戳2
        answer[8] = getCharValue(currentId, SETP3); // 填充时间戳3
        answer[7] = getCharValue(currentId, SETP4); // 填充时间戳4
        answer[6] = getCharValue(currentId, SETP5); // 填充时间戳5
        answer[5] = getCharValue(currentId, SETP6); // 填充时间戳6
        answer[4] = getCharValue(currentId, SETP7); // 填充时间戳7

        answer[3] = this.prefix3;
        answer[2] = this.prefix2;
        answer[1] = this.prefix1;
        answer[0] = this.prefix0;

        return new String(answer);
    }


    /**
     * 根据步进值获取对应的字典内容
     */
    private static char getCharValue(long value, long setp) {
        return DICT[(int) ((value / setp) % DICT.length)];
    }

    /**
     * 计算对应字符的数值
     */
    private static long getCharNum(char value, long setp) {
        for (int i = 0; i < DICT.length; i++) {
            if (DICT[i] == value) {
                return i * setp;
            }
        }

        return 0;
    }

    /**
     * 获取当前时间戳(单位秒)
     */
    private static long currentId() throws APIException {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 获取当前线程ID
     */
    private static long threadId() throws APIException {
        return Thread.currentThread().threadId() % SETP3;
    }

    /**
     * 获取序列号(基于SETP4循环)
     */
    private long serialId() throws APIException {
        if ((order += 1) >= SETP4) {
            return order = 0;//
        }

        return order;
    }

    /**
     * 获取序号生成器
     */
    public synchronized static UuidUtil load(long id) {
        UuidUtil record = local.get();

        // 连接不存在 | 连接被关闭
        if (record == null && id >= 0) {
            record = new UuidUtil(id);

            local.set(record);
        }

        return record;
    }

    /**
     * 从生成的UUID中获取对应的时间戳
     */
    public static long timestamp(String uuid) throws APIException {
        if (uuid != null && uuid.length() >= 12) {
            char[] chars = uuid.toCharArray();
            long answer = 0;

            answer += getCharNum(chars[11], SETP0);
            answer += getCharNum(chars[10], SETP1);
            answer += getCharNum(chars[9], SETP2);
            answer += getCharNum(chars[8], SETP3);
            answer += getCharNum(chars[7], SETP4);
            answer += getCharNum(chars[6], SETP5);
            answer += getCharNum(chars[5], SETP6);
            answer += getCharNum(chars[4], SETP7);

            return answer * 1000;
        }

        return 0;
    }

    /**
     * 生成唯一序列号: 服务号(1:MAX=35) + 线程号(3) + 时间戳(8) + 序列号(4)
     */
    public static String generate(long id) throws APIException {
        return load(id).generateCode();
    }
}
